
// <copyright file="CloneEnumerable.cs" company="Subhadra Software Solutions">
//   
// </copyright>
// <summary>
//   The clone enumerable.
// </summary>

namespace SubhadraSolutions.Sharp.Utils.Collections.Generic
{
    using System;
    using System.Collections;
    using System.Collections.Generic;

    using SubhadraSolutions.Sharp.Utils.Runtime.Serialization.Formatters.Binary;

    /// <summary>
    /// The clone enumerable.
    /// </summary>
    /// <typeparam name="T">
    /// </typeparam>
    [Serializable]
    public sealed class CloneEnumerable<T> : IEnumerable<T>
    {
        #region Fields

        /// <summary>
        /// The adaptedObject.
        /// </summary>
        private readonly IEnumerable<T> adaptedObject;

        #endregion Fields

        #region Constructors and Finalizers

        /// <summary>
        /// Initializes a new instance of the <see cref="CloneEnumerable{T}"/> class.
        /// </summary>
        /// <param name="adaptedObject">
        /// The adaptedObject.
        /// </param>
        public CloneEnumerable(IEnumerable<T> adaptedObject)
        {
            this.adaptedObject = adaptedObject;
        }

        #endregion Constructors and Finalizers

        #region Public Methods and Operators

        /// <summary>
        /// The get enumerator.
        /// </summary>
        /// <returns>
        /// The <see cref="IEnumerator"/>.
        /// </returns>
        public IEnumerator<T> GetEnumerator()
        {
            return new CloneEnumerator(this.adaptedObject.GetEnumerator());
        }

        #endregion Public Methods and Operators

        #region Explicit Interface Methods

        /// <summary>
        /// The get enumerator.
        /// </summary>
        /// <returns>
        /// The <see cref="IEnumerator"/>.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return new CloneEnumerator(this.adaptedObject.GetEnumerator());
        }

        #endregion Explicit Interface Methods

        /// <summary>
        /// The clone enumerator.
        /// </summary>
        private sealed class CloneEnumerator : IEnumerator<T>
        {
            #region Fields

            /// <summary>
            /// The adaptedObject.
            /// </summary>
            private readonly IEnumerator<T> adaptedObject;

            #endregion Fields

            #region Constructors and Finalizers

            /// <summary>
            /// Initializes a new instance of the <see cref="CloneEnumerator"/> class.
            /// </summary>
            /// <param name="adaptedObject">
            /// The adaptedObject.
            /// </param>
            public CloneEnumerator(IEnumerator<T> adaptedObject)
            {
                this.adaptedObject = adaptedObject;
            }

            #endregion Constructors and Finalizers

            #region Explicit Interface Properties

            /// <summary>
            /// Gets the current.
            /// </summary>
            object IEnumerator.Current
            {
                get
                {
                    return this.getCurrent();
                }
            }

            /// <summary>
            /// Gets the current.
            /// </summary>
            T IEnumerator<T>.Current
            {
                get
                {
                    return this.getCurrent();
                }
            }

            #endregion Explicit Interface Properties

            #region Public Methods and Operators

            /// <summary>
            /// The dispose.
            /// </summary>
            public void Dispose()
            {
                this.adaptedObject.Dispose();
            }

            /// <summary>
            /// The move next.
            /// </summary>
            /// <returns>
            /// The <see cref="bool"/>.
            /// </returns>
            public bool MoveNext()
            {
                return this.adaptedObject.MoveNext();
            }

            /// <summary>
            /// The reset.
            /// </summary>
            public void Reset()
            {
                this.adaptedObject.Reset();
            }

            #endregion Public Methods and Operators

            #region Methods

            /// <summary>
            /// The get current.
            /// </summary>
            /// <returns>
            /// The <see cref="T"/>.
            /// </returns>
            private T getCurrent()
            {
                T adaptedObjectCurrent = this.adaptedObject.Current;
                var deepCloneable = adaptedObjectCurrent as IDeepCloneable;
                if (deepCloneable != null)
                {
                    return (T)deepCloneable.DeepClone();
                }

                var cloneable = adaptedObjectCurrent as ICloneable;
                if (cloneable != null)
                {
                    return (T)cloneable.Clone();
                }

                return (T)adaptedObjectCurrent.DeepClone();
            }

            #endregion Methods
        }
    }
}